﻿using RabbitMQ.Client;
using System;
using RabbitMQ.Client.Events;
using log4net;

namespace Keede.RabbitMQ
{

    public abstract class BaseClient
    {
        readonly Authorization _auth;
        IConnection _connection;
        protected IModel _channel;
        protected ILog _log;
        protected bool _closed = false;

        protected BaseClient(Authorization auth, ushort prefetchCount)
        {

            this._log = LogManager.GetLogger(typeof(Client));
            _auth = auth;

            Connect(prefetchCount);
        }

        protected void Connect(ushort prefetchCount)
        {
            IConnectionFactory connectionFactory = new ConnectionFactory
            {
                UserName = _auth.UserName,
                Password = _auth.Password,
                VirtualHost = _auth.VHost,
                HostName = _auth.Host,
                Port = _auth.Port,
                AutomaticRecoveryEnabled = true,
                TopologyRecoveryEnabled = true,
                RequestedHeartbeat = 60,
                Protocol = Protocols.DefaultProtocol
            };
            _connection = connectionFactory.CreateConnection();
            _connection.ConnectionShutdown += _connection_ConnectionShutdown; ;
            _connection.CallbackException += _connection_CallbackException; ;
            _connection.ConnectionBlocked += _connection_ConnectionBlocked; ; ;
            _connection.ConnectionUnblocked += _connection_ConnectionUnblocked; ;

            _channel = _connection.CreateModel();
            _channel.BasicQos(0, prefetchCount, false);
            _channel.ModelShutdown += _model_ModelShutdown; ;
            _channel.CallbackException += _model_CallbackException;

            _log.Info("Rabbitmq server already connected.");
        }

        private void _model_ModelShutdown(object sender, ShutdownEventArgs e)
        {
            _log.Info($"Model shutdown. {e}");
        }

        private void _connection_ConnectionUnblocked(object sender, EventArgs e)
        {
            _log.Info($"Connection unblocked. {e}");
        }

        private void _connection_ConnectionBlocked(object sender, ConnectionBlockedEventArgs e)
        {
            _log.Info($"Connection blocked. {e}");
        }

        private void _connection_CallbackException(object sender, CallbackExceptionEventArgs e)
        {
            _log?.Error("Connection Callback is exception", e.Exception);
        }

        private void _model_CallbackException(object sender, CallbackExceptionEventArgs e)
        {
            _log?.Error("Model Callback is exception", e.Exception);
        }

        private void _connection_ConnectionShutdown(object sender, ShutdownEventArgs e)
        {
            _log.Info($"Connection shutdown.{e}");
        }

        #region dispose
        ~BaseClient()
        {
            Dispose(true);
        }

        private bool _alreadyDispose = false;

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        protected virtual void Dispose(bool disposing)
        {
            if (_alreadyDispose) return;

            if (disposing)
            {
                try
                {
                    _channel?.Dispose();
                    _connection.Dispose();
                }
                catch (ObjectDisposedException e)
                {
                    _log.Error("Dispose connection or channel error.", e);
                }
                this._closed = true;
            }

            _alreadyDispose = true;
            _log.Info("Connection has been disposed.");
        }
        #endregion
    }
}
